<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>

    <link rel="stylesheet" type="text/css" 
      href="/assets/css/straybirds.css" media="screen" />
    <link rel="stylesheet" type="text/css" 
      href="/assets/css/pygments.css" media="screen" />

    <!-- MathJax Section Start -->

    <script type="text/javascript"
    src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
    </script>
    <script>
        MathJax.Hub.Config({
              tex2jax: {
              skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
              }
        });
        MathJax.Hub.Queue(function() {
            var all = MathJax.Hub.getAllJax(), i;
            for(i=0; i < all.length; i += 1) {
                all[i].SourceElement().parentNode.className += ' has-jax';
            }
        });
    </script>

    <!-- MathJax Section End -->

    <!-- Google Analytics Start-->
    <script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-48100787-1', 'minixalpha.github.io');
  ga('send', 'pageview');

</script>

    <!-- Google Analytics End -->

    <title>OpenJDK 源码阅读之 Java 输入输出(I/O) 之 字节流输入</title>
  </head>

  <body>
    <div class="container">
      <header>
        <div class="container">
          <h1>
              <a href="/" title="Home Page"> 潇湘夜雨 </a>
          <span class="github-src">
            <a href ="https://github.com/minixalpha/minixalpha.github.io"
               target="_blank"
               title="Fork me on GitHub">
              <img src="/assets/images/GitHub-Mark-Light-32px.png" alt="">
            </a>
          </span>
          </h1>
        </div>
      </header>

      <aside id="left-side">
        <h2> 分类 </h2>
  <ul class="category-list">
      
            
                <li>
                <a href="/categories/源代码阅读"> 源代码阅读 (20) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/设计模式"> 设计模式 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/科研"> 科研 (6) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/思想"> 思想 (2) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/技术"> 技术 (18) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/工具"> 工具 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/虚拟机"> 虚拟机 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/java"> java (11) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/c语言"> c语言 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/读书"> 读书 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/操作系统"> 操作系统 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/英语"> 英语 (10) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/计算机系统"> 计算机系统 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/生活"> 生活 (1) </a>
                </li>
            
      
  </ul>

      </aside>

      <aside id="right-side">
        <h2> 归档 </h2>
  <ul class="archive-list">
    
    
    
        
        
        
        
            
            <li>
                <a href="/2014/08">
                    2014-08 (1)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/07">
                    2014-07 (5)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/05">
                    2014-05 (12)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/04">
                    2014-04 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/03">
                    2014-03 (11)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/02">
                    2014-02 (6)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/01">
                    2014-01 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/11">
                    2013-11 (10)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/10">
                    2013-10 (3)
                </a>
            </li>

        
        
    
        
        
        
        
            
            <li>
                <a href="/2010/09">
                    2010-09 (1)
                </a>
            </li>

        
        
    
  </ul>

      </aside>

      <article>

<h1>OpenJDK 源码阅读之 Java 输入输出(I/O) 之 字节流输入</h1>

<p>标签（空格分隔）： 源代码阅读 Java 封神之路</p>

<p>Java 的输入输出总是给人一种很混乱的感觉，要想把这个问题搞清楚，必须对各种与输入输出相关的类之间的关系有所了解。只有你了解了他们之间的关系，知道设计这个类的目的是什么，才能更从容的使用他们。</p>

<p>我们先对 Java I/O 的总体结构进行一个总结，再通过分析源代码，给出把每个类的关键功能是如何实现的。</p>

<h2>Java I/O 的主要结构</h2>

<p>Java 的输入输出，主要分为以下几个部分：</p>

<ul>
<li>字节流</li>
<li>字符流</li>
<li>Socket</li>
<li>新 I/O</li>
</ul>

<p>每个部分，都包含了输入和输出两部分。</p>

<h2>实现概要</h2>

<p>这里只给出每个类的实现概要，具体每个类的实现分析，可以参见我的 <a href="https://github.com/minixalpha/SourceLearning/tree/master/openjdk">GitHub-SourceLearning-OpenJDK</a> 页面。根据导航中的链接，进入 <a href="https://github.com/minixalpha/SourceLearning/tree/master/openjdk/notes/openjdk/jdk/src/share/classes/java/io">java.io</a> ，即可看到对每个类的分析。</p>

<h3>字节流输入</h3>

<p><img src="/assets/blog-images/java_io_read_bytes.png" alt="java_io_read_bytes"></p>

<p>图1 Java 字节输入类</p>

<ul>
<li>InputStream</li>
</ul>

<p>InputStream 是所有字节输入类的基类，它有一个未实现的 <code>read</code> 方法，子类需要实现这个 <code>read</code> 方法， 它和数据的来源相关。它的各种不同子类，或者是添加了功能，或者指明了不同的数据来源。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">abstract</span> <span class="kt">int</span> <span class="nf">read</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span><span class="o">;</span>
</code></pre></div>
<ul>
<li>ByteArrayInputStream</li>
</ul>

<p><code>ByteArrayInputStream</code> 有一个内部 <code>buffer</code> ， 包含从流中读取的字节，还有一个内部 <code>counter</code>， 跟踪下一个要读入的字节。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">protected</span> <span class="kt">byte</span> <span class="n">buf</span><span class="o">[];</span>
<span class="kd">protected</span> <span class="kt">int</span> <span class="n">pos</span><span class="o">;</span>
</code></pre></div>
<p>这个类在初始化时，需要指定一个 <code>byte[]</code>，作为数据的来源，它的 <code>read</code>，就读入这个 <code>byte[]</code> 中所包含的数据。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="nf">ByteArrayInputStream</span><span class="o">(</span><span class="kt">byte</span> <span class="n">buf</span><span class="o">[])</span> <span class="o">{</span>
    <span class="k">this</span><span class="o">.</span><span class="na">buf</span> <span class="o">=</span> <span class="n">buf</span><span class="o">;</span>
    <span class="k">this</span><span class="o">.</span><span class="na">pos</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="k">this</span><span class="o">.</span><span class="na">count</span> <span class="o">=</span> <span class="n">buf</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">int</span> <span class="nf">read</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">return</span> <span class="o">(</span><span class="n">pos</span> <span class="o">&lt;</span> <span class="n">count</span><span class="o">)</span> <span class="o">?</span> <span class="o">(</span><span class="n">buf</span><span class="o">[</span><span class="n">pos</span><span class="o">++]</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="o">)</span> <span class="o">:</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div>
<ul>
<li>FileInputStream</li>
</ul>

<p>FileInputStream 的数据来源是文件，即从文件中读取字节。初始化时，需要指定一个文件：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="nf">FileInputStream</span><span class="o">(</span><span class="n">File</span> <span class="n">file</span><span class="o">)</span> 
<span class="kd">throws</span> <span class="n">FileNotFoundException</span> <span class="o">{</span>
    <span class="n">String</span> <span class="n">name</span> <span class="o">=</span> <span class="o">(</span><span class="n">file</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">file</span><span class="o">.</span><span class="na">getPath</span><span class="o">()</span> <span class="o">:</span> <span class="kc">null</span><span class="o">);</span>
    <span class="n">SecurityManager</span> <span class="n">security</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">getSecurityManager</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">security</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">security</span><span class="o">.</span><span class="na">checkRead</span><span class="o">(</span><span class="n">name</span><span class="o">);</span>
    <span class="o">}</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">name</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">NullPointerException</span><span class="o">();</span>
    <span class="o">}</span>
    <span class="n">fd</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileDescriptor</span><span class="o">();</span>
    <span class="n">fd</span><span class="o">.</span><span class="na">incrementAndGetUseCount</span><span class="o">();</span>
    <span class="n">open</span><span class="o">(</span><span class="n">name</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>
<p>以后读取的数据，都来自于这个文件。这里的 <code>read</code> 方法是一个  <code>native</code> 方法，它的实现与操作系统相关。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">native</span> <span class="kt">int</span> <span class="nf">read</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span><span class="o">;</span>
</code></pre></div>
<ul>
<li>FilterInputStream</li>
</ul>

<p>FilterInputStream将其它输入流作为数据来源，其子类可以在它的基础上，对数据流添加新的功能。我们经常看到流之间的嵌套，以添加新的功能。就是在这个类的基础上实现的。所以，它的初始化中，会指定一个字节输入流：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java">    <span class="kd">protected</span> <span class="kd">volatile</span> <span class="n">InputStream</span> <span class="n">in</span><span class="o">;</span>
    <span class="kd">protected</span> <span class="nf">FilterInputStream</span><span class="o">(</span><span class="n">InputStream</span> <span class="n">in</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">in</span> <span class="o">=</span> <span class="n">in</span><span class="o">;</span>
    <span class="o">}</span>
</code></pre></div>
<p>读取操作，就依靠这个流实现：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kt">int</span> <span class="nf">read</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div>
<ul>
<li>BufferedInputStream</li>
</ul>

<p>BufferedInputStream 是 FilterInputStream 的子类，所以，需要给它提供一个底层的流，用于读取，而它本身，则为此底层流增加功能，即缓冲功能。以减少读取操作的开销，提升效率。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">protected</span> <span class="kd">volatile</span> <span class="kt">byte</span> <span class="n">buf</span><span class="o">[];</span>
</code></pre></div>
<p>内部缓冲区由一个 <code>volatile byte</code> 数组实现，大多线程环境下，一个线程向 <code>volatile</code> 数据类型中写入的数据，会立即被其它线程看到。</p>

<p><code>read</code> 操作会先看一下缓冲区里的数据是否已经全部被读取了，如果是，就调用底层流，填充缓冲区，再从缓冲区中按要求读取指定的字节。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">int</span> <span class="nf">read</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">pos</span> <span class="o">&gt;=</span> <span class="n">count</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">fill</span><span class="o">();</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">pos</span> <span class="o">&gt;=</span> <span class="n">count</span><span class="o">)</span>
            <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="nf">getBufIfOpen</span><span class="o">()[</span><span class="n">pos</span><span class="o">++]</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">getBufIfOpen</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="kt">byte</span><span class="o">[]</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">buf</span><span class="o">;</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">buffer</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span><span class="s">&quot;Stream closed&quot;</span><span class="o">);</span>
    <span class="k">return</span> <span class="n">buffer</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div>
<ul>
<li>DataInputStream</li>
</ul>

<p><code>DataInputStream</code> 也是 <code>FilterInputStream</code> 的子类，它提供的功能是：可以从底层的流中读取基本数据类型，例如 <code>int</code>, <code>char</code> 等等。<code>DataInputStream</code> 是非线程安全的， 你必须自己保证处理线程安全相关的细节。</p>

<p>例如，readBoolean 会读入一个字节，然后根据是否为0,返回 <code>true/false</code>。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="nf">readBoolean</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="n">ch</span> <span class="o">=</span> <span class="n">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">ch</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">EOFException</span><span class="o">();</span>
    <span class="k">return</span> <span class="o">(</span><span class="n">ch</span> <span class="o">!=</span> <span class="mi">0</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>
<p>readShort 会读入两个字节，然后拼接成一个 short 类型的数据。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">final</span> <span class="kt">short</span> <span class="nf">readShort</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="n">ch1</span> <span class="o">=</span> <span class="n">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
    <span class="kt">int</span> <span class="n">ch2</span> <span class="o">=</span> <span class="n">in</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">((</span><span class="n">ch1</span> <span class="o">|</span> <span class="n">ch2</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">EOFException</span><span class="o">();</span>
    <span class="k">return</span> <span class="o">(</span><span class="kt">short</span><span class="o">)((</span><span class="n">ch1</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="o">)</span> <span class="o">+</span> <span class="o">(</span><span class="n">ch2</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="o">));</span>
<span class="o">}</span>
</code></pre></div>
<p>int 和 long 依此类推，分别读入4个字节，8个字节，然后进行拼接。</p>

<p>但是，浮点数就不能通过简单的拼接来解决了，而要读入足够的字节数，然后再按照 IEEE 754 的标准进行解释：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">final</span> <span class="kt">float</span> <span class="nf">readFloat</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="k">return</span> <span class="n">Float</span><span class="o">.</span><span class="na">intBitsToFloat</span><span class="o">(</span><span class="n">readInt</span><span class="o">());</span>
<span class="o">}</span>
</code></pre></div>
<ul>
<li>PushbackInputstream</li>
</ul>

<p>PushbackInputstream 类也是FilterInputStream的子类，它提供的功能是，可以将已经读入的字节，再放回输入流中，下次读取时，可以读取到这个放回的字节。这在某些情境下是非常有用的。它的实现，就是依靠类似缓冲区的原理。被放回的字节，实际上是放在缓冲区里，读取时，先查看缓冲区里有没有字节，如果有就从这里读取，如果没有，就从底层流里读取。</p>

<p>缓冲区是一个字节数组：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">protected</span> <span class="kt">byte</span><span class="o">[]</span> <span class="n">buf</span><span class="o">;</span>
</code></pre></div>
<p>读取时，优先从这里读取，读不到，再从底层流读取。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kt">int</span> <span class="nf">read</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="n">ensureOpen</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">pos</span> <span class="o">&lt;</span> <span class="n">buf</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">buf</span><span class="o">[</span><span class="n">pos</span><span class="o">++]</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">read</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div>
<ul>
<li>PipedInputStream</li>
</ul>

<p>PipedInputStream 与 PipedOutputStream 配合使用，它们通过 <code>connect</code>  函数相关联。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kt">void</span> <span class="nf">connect</span><span class="o">(</span><span class="n">PipedOutputStream</span> <span class="n">src</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="n">src</span><span class="o">.</span><span class="na">connect</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>
<p>它们共用一个缓冲区，一个从中读取，一个从中写入。</p>

<p>PipedInputStream内部有一个缓冲区，</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">protected</span> <span class="kt">byte</span> <span class="n">buffer</span><span class="o">[];</span>
</code></pre></div>
<p>读取时，就从这里读：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kd">synchronized</span> <span class="kt">int</span> <span class="nf">read</span><span class="o">()</span>  <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(!</span><span class="n">connected</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span><span class="s">&quot;Pipe not connected&quot;</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">closedByReader</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span><span class="s">&quot;Pipe closed&quot;</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">writeSide</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">writeSide</span><span class="o">.</span><span class="na">isAlive</span><span class="o">()</span>
               <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">closedByWriter</span> <span class="o">&amp;&amp;</span> <span class="o">(</span><span class="n">in</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">))</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span><span class="s">&quot;Write end dead&quot;</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="n">readSide</span> <span class="o">=</span> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">();</span>
    <span class="kt">int</span> <span class="n">trials</span> <span class="o">=</span> <span class="mi">2</span><span class="o">;</span>
    <span class="k">while</span> <span class="o">(</span><span class="n">in</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">closedByWriter</span><span class="o">)</span> <span class="o">{</span>
            <span class="cm">/* closed by writer, return EOF */</span>
            <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
        <span class="o">}</span>
        <span class="k">if</span> <span class="o">((</span><span class="n">writeSide</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="o">(!</span><span class="n">writeSide</span><span class="o">.</span><span class="na">isAlive</span><span class="o">())</span> <span class="o">&amp;&amp;</span> <span class="o">(--</span><span class="n">trials</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">))</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span><span class="s">&quot;Pipe broken&quot;</span><span class="o">);</span>
        <span class="o">}</span>
        <span class="cm">/* might be a writer waiting */</span>
        <span class="n">notifyAll</span><span class="o">();</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="n">wait</span><span class="o">(</span><span class="mi">1000</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">InterruptedException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="n">java</span><span class="o">.</span><span class="na">io</span><span class="o">.</span><span class="na">InterruptedIOException</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
    <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">buffer</span><span class="o">[</span><span class="n">out</span><span class="o">++]</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="o">;</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">out</span> <span class="o">&gt;=</span> <span class="n">buffer</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">out</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">in</span> <span class="o">==</span> <span class="n">out</span><span class="o">)</span> <span class="o">{</span>
        <span class="cm">/* now empty */</span>
        <span class="n">in</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">ret</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div>
<p>过程比我们想的要复杂，因为这涉及两个线程，需要相互配合，所以，需要检查很多东西，才能最终从缓冲区中读到数据。</p>

<p>PipedOutputStream 类写入时，会调用 PipedInputStream 的receive功能，把数据写入 PipedInputStream 的缓冲区。</p>

<p>我们看一下 <code>PipedOutputStream.write</code>  函数：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">public</span> <span class="kt">void</span> <span class="nf">write</span><span class="o">(</span><span class="kt">int</span> <span class="n">b</span><span class="o">)</span>  <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">sink</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span><span class="s">&quot;Pipe not connected&quot;</span><span class="o">);</span>
    <span class="o">}</span>
    <span class="n">sink</span><span class="o">.</span><span class="na">receive</span><span class="o">(</span><span class="n">b</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>
<p>可以看出，调用了相关联的管道输入流的 <code>receive</code> 函数。</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kd">protected</span> <span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">receive</span><span class="o">(</span><span class="kt">int</span> <span class="n">b</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
    <span class="n">checkStateForReceive</span><span class="o">();</span>
    <span class="n">writeSide</span> <span class="o">=</span> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">in</span> <span class="o">==</span> <span class="n">out</span><span class="o">)</span>
        <span class="n">awaitSpace</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">in</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">in</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
        <span class="n">out</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="n">buffer</span><span class="o">[</span><span class="n">in</span><span class="o">++]</span> <span class="o">=</span> <span class="o">(</span><span class="kt">byte</span><span class="o">)(</span><span class="n">b</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">in</span> <span class="o">&gt;=</span> <span class="n">buffer</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">in</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p><code>receive</code> 的主要功能，就是把写入的数据放入缓冲区内。</p>

<p>注意注意的是，这两个类相互关联的对象，应该属于两个不同的线程，否则，容易造成死锁。</p>

<h2>扩展阅读</h2>

<ul>
<li><p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-javaio/">深入分析 Java I/O 的工作机制</a></p></li>
<li><p><a href="http://www.cnblogs.com/oubo/archive/2012/01/06/2394638.html">Java IO流学习总结</a></p></li>
</ul>


      </article>

      <div class="comments">
        
          <div id="disqus_thread"></div>
 <script type="text/javascript">
     /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
     var disqus_shortname = 'minixalpha'; // required: replace example with your forum shortname

     /* * * DON'T EDIT BELOW THIS LINE * * */
     (function() {
         var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
    

        
      </div>


      <footer>
        Copyright (c) minixalpha 2014
      </footer>

    </div>
  </body>
</html>
